using System;
using System.Collections.Generic;
using System.Configuration;
using System.Text;
using System.Collections;
using System.Diagnostics;

namespace CompanyX.TechnicalArchitecture.PerfContext
{
    /// <summary>
    /// Context manager for performance counters (cannot be instantiated)
    /// <example>
    /// <code>
    /// public class UsePerfContext
    /// {
    ///     public void InstrumentedTransaction()
    ///     {
    ///         // &lt;TransactionName&gt; should be defined in the configuration file
    ///         using (IPerfContext perfContext = PerfContextManager.GetCurrentContext("&lt;TransactioName&gt;"))
    ///         {
    ///             try
    ///             {
    ///                 // Application code
    ///             }
    ///             catch (Exception /* e */)
    ///             {
    ///                 perfContext.ContextInError = true;
    ///             }
    ///         } // All configured performance counter like TPS, ResponseTime, Count and/or Errors will be updated 
    ///           // automatically based on configuration setting
    ///     }
    /// }
    /// </code>
    /// </example>
    /// </summary>
    public static class PerfContextManager
    {
        /// <summary>
        /// Internal cache data structure
        /// </summary>
        internal struct CounterCacheItem
        {
            private string instanceNameValue;
            public string InstanceName
            {
                get { return instanceNameValue; }
                set { instanceNameValue = value; }
            }

            private PerformanceCounterType counterTypeValue;
            public PerformanceCounterType CounterType
            {
                get { return counterTypeValue; }
                set { counterTypeValue = value; }
            }

            private PerformanceCounter counter;
            public PerformanceCounter Counter
            {
                get { return counter; }
                set { counter = value; }
            }

            private bool IsErrorCounterValue;
            public bool IsErrorCounter
            {
                get { return IsErrorCounterValue; }
                set { IsErrorCounterValue = value; }
            }
	
        }

        /// <summary>
        /// Counter category data - cached to enable cleanup during shutdown
        /// </summary>
        private static Hashtable counterCreationData = new Hashtable();

        /// <summary>
        /// Cached counters
        /// </summary>
        private static Hashtable counterCache = new Hashtable();

        /// <summary>
        /// Initialization flag
        /// </summary>
        private static bool initialized = false;

        /// <summary>
        /// Lock object for synchronization
        /// </summary>
        private static object lockObject = new object();

        private const string COUNTERNAME_SUFFIX_EXECUTIONTIME = "ExecutionTime";
        private const string COUNTERNAME_SUFFIX_THROUGHPUT = "Throughput";
        private const string COUNTERNAME_SUFFIX_EXECUTIONCOUNT = "ExecutionCount";
        private const string COUNTERNAME_SUFFIX_EXECUTIONERRORCOUNT = "ExecutionErrorCount";

        /// <summary>
        /// Type initializer
        /// </summary>
        static PerfContextManager()
        {
            Initialize();
        }

        /// <summary>
        /// Create performance counters and instances based on configuration and hooks a process exit handler
        /// to clean up the counters when done
        /// </summary>
        /// <remarks>Requires write access to PerfMon</remarks>
        public static void Initialize()
        {
            if (!initialized) // Optimistic check
            {
                lock (lockObject)
                {
                    if (!initialized) // Concurrency check
                    {
                        PerfContextsConfig config = (PerfContextsConfig)ConfigurationManager.GetSection("appSpecificPerfContexts");
                        if (config != null)
                        {
                            AppDomain.CurrentDomain.ProcessExit += new EventHandler(UnInitialize);
                            CreatePerformanceCounterCategory(config);
                            CreatePerformanceCounters(config);
                        }
                        initialized = true;
                    }
                }
            }
        }

        /// <summary>
        /// Cleanup custom performance counters
        /// </summary>
        /// <remarks>Requires write access to PerfMon</remarks>
        public static void UnInitialize()
        {
            UnInitialize(null, null);
        }

        private static void UnInitialize(object sender, EventArgs e)
        {
            // Create performance counter categories based on collated counter creation data
            if (initialized)
            {
                initialized = false;
                IEnumerator iterator = counterCreationData.Keys.GetEnumerator();
                iterator.Reset();
                while (iterator.MoveNext())
                {
                    string categoryName = (string)iterator.Current;
                    if (PerformanceCounterCategory.Exists(categoryName))
                        PerformanceCounterCategory.Delete(categoryName);
                }
                
            }
        }

        /// <summary>
        /// Based on the configuration, dynamically create all the necessary performance counter
        /// categories along with their counters
        /// </summary>
        /// <param name="config">Configuration settings to build performance counter categories from</param>
        /// <remarks>
        /// Makes all the necessary counter creation data in a type-level Hashtable available for cleanup
        /// during shutdown
        /// </remarks>
        private static void CreatePerformanceCounterCategory(PerfContextsConfig config)
        {
            foreach (PerfContextConfig configItem in config.PerfContexts)
            {
                // Collate counters based on category
                if (!counterCreationData.ContainsKey(configItem.CategoryName))
                    counterCreationData.Add(configItem.CategoryName, new CounterCreationDataCollection());

                CounterCreationDataCollection counters = (CounterCreationDataCollection)counterCreationData[configItem.CategoryName];
                CounterCreationData counter = null;

                // Add counter creation data for average execution time
                if (configItem.ExecutionTimeCounter)
                {
                    counter = new CounterCreationData();
                    counter.CounterName = string.Format("{0}.{1}", configItem.ContextName, COUNTERNAME_SUFFIX_EXECUTIONTIME);
                    counter.CounterHelp = string.Format("Average execution time for {0} in secs", configItem.ContextName);
                    counter.CounterType = PerformanceCounterType.AverageTimer32;

                    counters.Add(counter);

                    counter = new CounterCreationData();
                    counter.CounterName = string.Format("{0}.{1} (Base)", configItem.ContextName, COUNTERNAME_SUFFIX_EXECUTIONTIME);
                    counter.CounterHelp = string.Format("Average execution time for {0} in secs", configItem.ContextName);
                    counter.CounterType = PerformanceCounterType.AverageBase;

                    counters.Add(counter);
                }

                // Add counter creation data for throughput
                if (configItem.ThroughputCounter)
                {
                    counter = new CounterCreationData();
                    counter.CounterName = string.Format("{0}.{1}", configItem.ContextName, COUNTERNAME_SUFFIX_THROUGHPUT);
                    counter.CounterHelp = string.Format("Number of {0} executing per second", configItem.ContextName);
                    counter.CounterType = PerformanceCounterType.RateOfCountsPerSecond64;

                    counters.Add(counter);
                }

                // Add counter creation data for execution count
                if (configItem.ExecutionCountCounter)
                {
                    counter = new CounterCreationData();
                    counter.CounterName = string.Format("{0}.{1}", configItem.ContextName, COUNTERNAME_SUFFIX_EXECUTIONCOUNT);
                    counter.CounterHelp = string.Format("Total number of {0} executions", configItem.ContextName);
                    counter.CounterType = PerformanceCounterType.NumberOfItems64;

                    counters.Add(counter);
                }

                // Add counter creation data for execution count
                if (configItem.ExecutionErrorCountCounter)
                {
                    counter = new CounterCreationData();
                    counter.CounterName = string.Format("{0}.{1}", configItem.ContextName, COUNTERNAME_SUFFIX_EXECUTIONERRORCOUNT);
                    counter.CounterHelp = string.Format("Total number of {0} executions that failed", configItem.ContextName);
                    counter.CounterType = PerformanceCounterType.NumberOfItems64;

                    counters.Add(counter);
                }
            }

            // Create performance counter categories based on collated counter creation data
            IEnumerator iterator = counterCreationData.Keys.GetEnumerator();
            iterator.Reset();
            while (iterator.MoveNext())
            {
                string categoryName = (string)iterator.Current;
                string categoryHelp = string.Format("Performance counters for {0}", categoryName);
                CounterCreationDataCollection counters = counterCreationData[categoryName] as CounterCreationDataCollection;
                if ((!PerformanceCounterCategory.Exists(categoryName)) && (counters != null))
                    PerformanceCounterCategory.Create(categoryName, categoryHelp, PerformanceCounterCategoryType.MultiInstance, counters);
            }

        }

        /// <summary>
        /// Based on the configuration, dynamically creates all the counter instances
        /// </summary>
        /// <param name="config">Configuration settings to build performance counter categories from</param>
        /// <remarks>
        /// Makes all the necessary counter instances in a type-level Hashtable use during runtime
        /// </remarks>
        private static void CreatePerformanceCounters(PerfContextsConfig config)
        {
            // Iterate and build counter cache for each context
            foreach (PerfContextConfig configItem in config.PerfContexts)
            {
                ArrayList cacheItems = new ArrayList();
                string[] instanceNames = configItem.InstanceNames.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
                
                // Get all the counters for the category that this context belongs to and filter by context name
                // to create specified instance(s)
                CounterCreationDataCollection counters = (CounterCreationDataCollection)counterCreationData[configItem.CategoryName];
                foreach (CounterCreationData counter in counters)
                {
                    // Need only instances for the current context
                    if (counter.CounterName.StartsWith(configItem.ContextName))
                    {
                        // Parse instance names that are comma separated
                        foreach (string instanceName in instanceNames)
                        {
                            CounterCacheItem cacheItem = new CounterCacheItem();
                            cacheItem.InstanceName = instanceName;
                            cacheItem.CounterType = counter.CounterType;
                            cacheItem.Counter = new PerformanceCounter(configItem.CategoryName, counter.CounterName, instanceName, false);
                            cacheItem.IsErrorCounter = (counter.CounterName.EndsWith(COUNTERNAME_SUFFIX_EXECUTIONERRORCOUNT)) ? true : false;
                            cacheItems.Add(cacheItem);
                        }
                    }
                }
                counterCache.Add(configItem.ContextName, cacheItems);
            }
        }

        /// <summary>
        /// Provides a list of context-specific performance counters 
        /// </summary>
        /// <param name="context">Name of the performance context - must match that of configuration</param>
        /// <returns>An array list of performance counters</returns>
        /// <remarks>Note this is for internal use only - the array list should be treated as read only</remarks>
        internal static ArrayList GetContextCounters(string context)
        {
            return counterCache[context] as ArrayList;
        }

        /// <summary>
        /// Gets the specified performance context object
        /// </summary>
        /// <param name="contextName">Name of the performance context - must match that of configuration</param>
        /// <returns>Performance context object with automatic ability to update the appropriate counters of the context</returns>
        public static IPerfContext GetCurrentContext(string contextName)
        {
            return new PerfContext(contextName);
        }

        /// <summary>
        /// Gets the specified performance context object
        /// </summary>
        /// <param name="contextName">Name of the performance context - must match that of configuration</param>
        /// <param name="instanceName">Performance context object with automatic ability to update the appropriate counters of the context</param>
        /// <returns></returns>
        public static IPerfContext GetCurrentContext(string contextName, string instanceName)
        {
            return new PerfContext(contextName, instanceName);
        }
    }
}
